﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Icms.Core;
using Icms.Core.Model;
using Icms.Core.Repository;
using Icms.Core.Service;
using Icms.Core.Utils;
using Omu.ValueInjecter;

namespace Icms.Service
{
    public class ParticipantService : ICrudService<Participant>
    {
        private readonly IRepo<Person> personRepo;
        private readonly IRepo<Party> partyRepo;

        public ParticipantService(IRepo<Person> personRepo, IRepo<Party> partyRepo)
        {
            this.personRepo = personRepo;
            this.partyRepo = partyRepo;
        }

        public Guid Create(Participant e)
        {
            var party = new Party { DateAdd = DateTime.Now };
            party.InjectFrom(e).InjectFrom<NullablesToNormal>(e);

            if (e.PersonId.HasValue && AreSame(personRepo.Get(e.PersonId.Value), e))
            {
                party.PersonId = e.PersonId.Value;
            }
            else
            {
                var person = new Person();
                person.InjectFrom(new Same("Id", "PersonId"), e);
                personRepo.Insert(person);
                personRepo.Save();

                party.PersonId = person.Id;
            }

            partyRepo.Insert(party);
            partyRepo.Save();
            return party.Id;
        }
        
        public void Save(Participant o)
        {
            var party = partyRepo.Get(o.Id);
            party.InjectFrom(o).InjectFrom<NullablesToNormal>(o);

            if(party.PersonId == Guid.Empty) throw new IcmsException("PersonId can't be empty");

            if (!AreSame(personRepo.Get(party.PersonId), o))
            {
                var person = new Person();
                person.InjectFrom(new Same("Id", "PersonId"), o);
                person.PersonId = o.PersonId;
                personRepo.Insert(person);
                personRepo.Save();
                party.PersonId = person.Id;
            }

            partyRepo.Save();
        }

        public void Delete(Guid id)
        {
            partyRepo.Delete(partyRepo.Get(id));
        }

        public Participant Get(Guid id)
        {
            var o = new Participant();
            var party = partyRepo.Get(id);
            o.InjectFrom(new Same("Id", "Party"), party.Person);
            o.InjectFrom<NormalToNullables>(party).InjectFrom(party);

            return o;
        }

        public IEnumerable<Participant> GetAll()
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Participant> Where(Expression<Func<Participant, bool>> predicate)
        {
            throw new NotImplementedException();
        }

        private static bool AreSame(Person person, Participant participant)
        {
            for (var i = 0; i < person.GetProps().Count; i++)
            {
                var p = person.GetProps()[i];
                if (p.Name == "Id" || p.Name == "PersonId") continue;
                for (var j = 0; j < participant.GetProps().Count; j++)
                {
                    var pa = participant.GetProps()[j];
                    if ((p.Name == pa.Name))
                    {
                        var v1 = p.GetValue(person);
                        var v2 = pa.GetValue(participant);
                        if (null == v1 && null == v2) continue;
                        if (v1 == null) return false;

                        if (!v1.Equals(v2))
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
}